﻿using System;
using System.Collections.Generic;
using System.Linq;
using System.Text;
using System.IO;
using OpenTK.Graphics;
using OpenTK.Graphics.OpenGL;
using OpenTK;
using System.Drawing;
using System.Drawing.Imaging;
using System.Text.RegularExpressions;
using System.Runtime.InteropServices;

namespace testopenTk
{
    /// <summary>
    /// 为加载模型而设计的类
    /// </summary>
    public class LoadObj : IDisposable
    {
     
        public struct ObjInfo
        {
            public bool bIsTextured;                        //是否使用纹理
            public List<Vector3> Verts;                  //顶点数组
            public List<Vector3> Normals;
            public List<Vector3> Texcoords;
            public int _Texture;                            //贴图
            public string name;
            public int Vao;
            public int VertVbo;
            public int NorVbo;
            public int TexcVbo;
        }
        /// <summary>
        /// 模型的信息
        /// </summary>
        public ObjInfo ModelInfo;

        List<Vector3> vtemp = new List<Vector3>();
        List<Vector3> ntemp = new List<Vector3>();
        List<Vector3> ttemp = new List<Vector3>();

        public double radius = 0;
        Vector3 Box_min = new Vector3(), Box_max = new Vector3();
        //int testvbo = 0;
        //Vector3 testa = new Vector3(0, 0, 0), testb = new Vector3(11, 1, 1);
        /// <summary>
        /// 加载模型
        /// </summary>
        /// <param name="FilePath">路径及文件名，包含后缀</param>
        public LoadObj(String FilePath)
        {
            ModelInfo.Verts = new List<Vector3>();
            ModelInfo.Normals = new List<Vector3>();
            ModelInfo.Texcoords = new List<Vector3>();

            #region 读取文本内容
            using (StreamReader sr = new StreamReader(FilePath))
            {
                String stb = "";
                stb = (sr.ReadLine());
                while (stb != null)
                {
                    if (stb.ToString() == "")
                    {
                        stb = (sr.ReadLine());
                        continue;
                    }
                    switch (stb[0])
                    {
                        case 'm':

                            using (StreamReader streamR = new StreamReader("Res/" + stb.Split(' ')[1]))
                            {
                                string TexturePath = streamR.ReadLine();
                                while (TexturePath != null)
                                {
                                    TexturePath = TexturePath.Trim();
                                    if (TexturePath != "")
                                    {
                                        if (TexturePath[0] == 'm')
                                        {
                                            ModelInfo._Texture= LoadTexture("Res/" + TexturePath.Split(' ')[1]);
                                            break;
                                        }
                                    }
                                    TexturePath = streamR.ReadLine();
                                }
                            }
                            break;
                        case 'v':
                            if (stb[1] == ' ')
                                ReadArray(ref ModelInfo.Verts, stb.Remove(0, 3));
                            if (stb[1] == 't')
                                ReadArray(ref ModelInfo.Texcoords, stb.Remove(0, 3));
                            if (stb[1] == 'n')
                                ReadArray(ref ModelInfo.Normals, stb.Remove(0, 3));
                            break;
                        case 'f':
                            string[] temp = stb.Remove(0, 2).Trim().Split(' ');
                            //读取索引
                            if (temp.Length == 3)
                            {
                                readFace(temp, 0, 1, 2);
                            }
                            if (temp.Length == 4)
                            {
                                readFace(temp, 0, 1, 2);
                                readFace(temp, 0, 2, 3);
                            }

                            break;

                    }
                    stb = (sr.ReadLine());
                }
            }
            #endregion
            ModelInfo.Verts = vtemp;
            ModelInfo.Normals = ntemp;
            ModelInfo.Texcoords = ttemp;
            VBO();
            radius = VectorDistance(Box_min, Box_max)/2.0;
            //MyMessage.lable4Show(radius.ToString());
        }
        //读取索引
        void readFace(string[] temp, int a, int b, int c)
        {
            for (int i = 0; i < temp.Length; i++)
            {
                if (i != a && i != b && i != c)
                    continue;
                string[] t = temp[i].Split('/');
                if (t.Length == 3)//  v/t/n v//n
                {
                    if (t[1] != "")
                    {
                        vtemp.Add(ModelInfo.Verts[int.Parse(t[0]) - 1]);
                        ttemp.Add(ModelInfo.Texcoords[int.Parse(t[1]) - 1]);
                        ntemp.Add(ModelInfo.Normals[int.Parse(t[2]) - 1]);
                    }
                    else
                    {
                        vtemp.Add(ModelInfo.Verts[int.Parse(t[0]) - 1]);
                        ntemp.Add(ModelInfo.Normals[int.Parse(t[2]) - 1]);
                    }
                }
                if (t.Length == 2)
                {
                    vtemp.Add(ModelInfo.Verts[int.Parse(t[0]) - 1]);
                    ttemp.Add(ModelInfo.Texcoords[int.Parse(t[1]) - 1]);
                }
                if (t.Length == 1)
                    vtemp.Add(ModelInfo.Verts[int.Parse(t[0]) - 1]);
            }
        }
        //读取v,vn,vt
        void ReadArray(ref List<Vector3> array, String str)
        {
            string[] temp = str.Split(' ');
            float x = float.Parse(temp[0]);
            float y = float.Parse(temp[1]);
            float z = float.Parse(temp[2]);
            if (array.Equals(ModelInfo.Verts))
            {
                if (ModelInfo.Verts.Count == 0)
                {
                    Box_min = new Vector3(x,y,z);
                    Box_max = new Vector3(x, y, z);
                }
                else
                {
                    Box_min.X = x < Box_min.X ? x : Box_min.X;
                    Box_min.Y = y < Box_min.Y ? y : Box_min.Y;
                    Box_min.Z = z < Box_min.Z ? z : Box_min.Z;

                    Box_max.X = x > Box_max.X ? x : Box_max.X;
                    Box_max.Y = y > Box_min.Y ? y : Box_max.Y;
                    Box_max.Z = z > Box_min.Z ? z : Box_max.Z;
                }
            }
            array.Add(new Vector3(x, y, z));

        }
        static int LoadTexture(string TexPath)//加载纹理
        {
            if (String.IsNullOrEmpty(TexPath))
                return 0;
            //MyMessage.show(TexPath);
            //GL.Enable(EnableCap.Texture2D);
            //GL.Hint(HintTarget.PerspectiveCorrectionHint, HintMode.Nicest);
            Bitmap bm = new Bitmap(TexPath/*"Res/1371714170_1812223777.gif"*/);
            int _Texture = GL.GenTexture();
            GL.BindTexture(TextureTarget.Texture2D, _Texture);

            System.Drawing.Imaging.BitmapData bmd = bm.LockBits(new Rectangle(0, 0, bm.Width, bm.Height),
                System.Drawing.Imaging.ImageLockMode.ReadOnly,
                System.Drawing.Imaging.PixelFormat.Format32bppArgb);
            GL.TexImage2D(TextureTarget.Texture2D,
                0,
                PixelInternalFormat.Rgba,
                bm.Width,
                bm.Height,
                0,
                OpenTK.Graphics.OpenGL.PixelFormat.Bgra,
                PixelType.UnsignedByte,
                bmd.Scan0);
            bm.UnlockBits(bmd);


            GL.GenerateMipmap(GenerateMipmapTarget.Texture2D);
            GL.TexParameter(TextureTarget.Texture2D, TextureParameterName.TextureMinFilter, (int)TextureMinFilter.Linear);
            GL.TexParameter(TextureTarget.Texture2D, TextureParameterName.TextureMagFilter, (int)TextureMinFilter.Linear);
            //MyMessage.show(ModelInfo._Texture.ToString());
            //MyMessage.lableShow(bm.Height.ToString());
            return _Texture;
        }
        //使用vbo
        void VBO()
        {
            #region 托管->非托管
            //byte[] bytes = new byte[10] { 0 };//你的二进制数组
            //IntPtr ptr = Marshal.AllocHGlobal(bytes.Length);
            //Marshal.Copy(bytes, 0, ptr, bytes.Length);

            #endregion
            #region old_VBO
            //ModelInfo.Vao = GL.GenVertexArray();
            //GL.BindVertexArray(ModelInfo.Vao);
            //ModelInfo.VertVbo = GL.GenBuffer();
            //GL.BindBuffer(BufferTarget.ArrayBuffer, ModelInfo.VertVbo);
            //GL.BufferData(BufferTarget.ArrayBuffer, (IntPtr)(ModelInfo.Verts.Count * sizeof(float) * 3),
            //    ModelInfo.Verts.ToArray(), BufferUsageHint.StaticDraw);
            //GL.EnableClientState(ArrayCap.VertexArray);
            //GL.VertexPointer(3, VertexPointerType.Float, 0, 0);

            ////GL.EnableVertexAttribArray(Shader.vPosition);
            ////GL.VertexAttribPointer(Shader.vPosition, 3, VertexAttribPointerType.Float, false, 0, 0);
            //ModelInfo.NorVbo = GL.GenBuffer();
            //GL.BindBuffer(BufferTarget.ArrayBuffer, ModelInfo.NorVbo);
            //GL.BufferData(BufferTarget.ArrayBuffer, (IntPtr)(ModelInfo.Normals.Count * sizeof(float) * 3),
            //    ModelInfo.Normals.ToArray(), BufferUsageHint.StaticDraw);
            //GL.EnableClientState(ArrayCap.NormalArray);
            //GL.NormalPointer(NormalPointerType.Float, 0, 0);
            ////GL.EnableVertexAttribArray(Shader.vNormal);
            ////GL.VertexAttribPointer(Shader.vNormal, 3, VertexAttribPointerType.Float, false, 0, 0);

            //ModelInfo.TexcVbo = GL.GenBuffer();
            //GL.BindBuffer(BufferTarget.ArrayBuffer, ModelInfo.TexcVbo);
            //GL.BufferData(BufferTarget.ArrayBuffer, (IntPtr)(ModelInfo.Texcoords.Count * sizeof(float) * 3),
            //    ModelInfo.Texcoords.ToArray(), BufferUsageHint.StaticDraw);
            //GL.EnableClientState(ArrayCap.TextureCoordArray);
            //GL.TexCoordPointer(3, TexCoordPointerType.Float, 0, 0);
            ////GL.EnableVertexAttribArray(Shader.vTexCoord);
            ////GL.VertexAttribPointer(Shader.vTexCoord, 3, VertexAttribPointerType.Float, false, 0, 0);


            //GL.BindVertexArray(0);
            #endregion
            #region VBO_subdata
            ModelInfo.Vao = GL.GenVertexArray();
            GL.BindVertexArray(ModelInfo.Vao);
            ModelInfo.VertVbo = GL.GenBuffer();
            GL.BindBuffer(BufferTarget.ArrayBuffer, ModelInfo.VertVbo);
            int vertSize = (ModelInfo.Verts.Count * sizeof(float) * 3);
            int norSize = (ModelInfo.Normals.Count * sizeof(float) * 3);
            int TexSize = (ModelInfo.Texcoords.Count * sizeof(float) * 3);
            int total = vertSize + norSize + TexSize;

            GL.BufferData(BufferTarget.ArrayBuffer, (IntPtr)total, IntPtr.Zero
                , BufferUsageHint.StaticDraw);

            GL.BufferSubData(BufferTarget.ArrayBuffer, (IntPtr)(0), (IntPtr)vertSize, ModelInfo.Verts.ToArray());
            GL.BufferSubData(BufferTarget.ArrayBuffer, (IntPtr)(vertSize), (IntPtr)norSize, ModelInfo.Normals.ToArray());
            GL.BufferSubData(BufferTarget.ArrayBuffer, (IntPtr)(vertSize + norSize), (IntPtr)TexSize, ModelInfo.Texcoords.ToArray());
            GL.EnableVertexAttribArray(Shader.vPosition);
            GL.VertexAttribPointer(Shader.vPosition, 3, VertexAttribPointerType.Float, false, 0, 0);
            GL.EnableVertexAttribArray(Shader.vNormal);
            GL.VertexAttribPointer(Shader.vNormal, 3, VertexAttribPointerType.Float, false, 0, vertSize);
            GL.EnableVertexAttribArray(Shader.vTexCoord);
            GL.VertexAttribPointer(Shader.vTexCoord, 3, VertexAttribPointerType.Float, false, 0, vertSize + norSize);
            //GL.EnableClientState(ArrayCap.VertexArray);
            //GL.VertexPointer(3, VertexPointerType.Float, 0, 0);
            //GL.EnableClientState(ArrayCap.NormalArray);
            //GL.NormalPointer(NormalPointerType.Float, 0, vertSize);
            //GL.EnableClientState(ArrayCap.TextureCoordArray);
            //GL.TexCoordPointer(3, TexCoordPointerType.Float, 0, vertSize + norSize);
            #endregion
         
            GL.BindVertexArray(0);
            //testvbo = GL.GenBuffer();
            //GL.BindBuffer(BufferTarget.ArrayBuffer, testvbo);
            //List<Vector3> a = new List<Vector3>();
            //a.Add(testa); a.Add(testb);
            //GL.BufferData(BufferTarget.ArrayBuffer, (IntPtr)(3 * sizeof(float) * 2), a.ToArray(), BufferUsageHint.StaticDraw);
            //GL.EnableVertexAttribArray(Shader.vPosition);
            //GL.VertexAttribPointer(Shader.vPosition, 2, VertexAttribPointerType.Float, false, 0, 0);

        }
        /// <summary>
        /// 绘制此模型
        /// </summary>
        internal void Draw()
        {
            GL.BindVertexArray(ModelInfo.Vao);
            GL.BindTexture(TextureTarget.Texture2D, ModelInfo._Texture);
            GL.DrawArrays(PrimitiveType.Triangles, 0, ModelInfo.Verts.Count);
           
        }

        double VectorDistance(/*ref Vector3 []result,*/Vector3 min,Vector3 max)
        {
            return Math.Sqrt(Math.Pow(max.X - min.X, 2.0) + Math.Pow(max.Y - min.Y, 2.0) + Math.Pow(max.Z - min.Z, 2.0));
          
                
        }

        public void Dispose()
        {
            if (ModelInfo.TexcVbo > 0)
            {
                GL.DeleteBuffer(ModelInfo.TexcVbo);
            }
            if (ModelInfo.NorVbo > 0)
            {
                GL.DeleteBuffer(ModelInfo.NorVbo);
            }
            if (ModelInfo.VertVbo > 0)
            {
                GL.DeleteBuffer(ModelInfo.VertVbo);
            }
            if (ModelInfo._Texture > 0)
            {
                GL.DeleteTexture(ModelInfo._Texture);
            }
        }
    }
}
